import json
import threading
from typing import List, AnyStr, Optional, Dict

import pytest
from _pytest.reports import TestReport

from config.globalVars import configs
import time

TestCase = pytest.mark.TestCase(level='', casename='', module='')


class Result:
    setup = None
    call = None
    teardown = None

    def __str__(self):
        if self.setup is True and self.call is True and self.teardown is True:
            return "Success"
        elif self.setup is True and self.call is True and not self.teardown:
            return "Error"
        elif not self.setup and self.call is None and self.teardown is None:
            return "Error"
        else:
            return "Failed"


class TaskDetail:
    _instance_lock = threading.Lock()

    def __new__(cls, *args, **kwargs):
        if not hasattr(TaskDetail, "_instance"):
            with TaskDetail._instance_lock:
                if not hasattr(TaskDetail, "_instance"):
                    TaskDetail._instance = object.__new__(cls)
        return TaskDetail._instance

    def __init__(self):
        self.start_time: int = 0
        self.end_time: int = 0
        self.cases = list()
        self.env: AnyStr = ''
        self.current_case: Optional[CaseDetail] = None

    def case_start(self):
        self.current_case = CaseDetail.__new__(CaseDetail)
        self.current_case.__init__()

    def case_end(self):
        self.cases.append(self.current_case.__str__())
        self.current_case = None

    def set_duration(self):
        self.current_case.duration = self.current_case.start_time - self.current_case.end_time

    def task_end(self):
        self.end_time = int(round(time.time()) * 1000)
        if self.env != 'test':
            result = dict()
            result['cases'] = self.cases
            result['duration'] = self.end_time - self.start_time
            result['startTime'] = self.start_time
            result['endTime'] =  self.end_time
            success_count = [i for i in self.cases if i.get("result") == "Success"]
            result['result'] = "Success" if len(success_count) and len(self.cases) and len(success_count) == len(self.cases) else "Failed"
            result['passRent'] = len(success_count) / len(self.cases) if self.cases else None
            with open('./result.json', 'w') as f:
                f.write(json.dumps(result))


class CaseDetail:

    def __init__(self):
        self._start_time: int = 0
        self._end_time: int = 0
        self._level: AnyStr = '1'
        self._result: Result = Result()
        self._case_name: AnyStr = ''
        self._function_name: AnyStr = ''
        self._case_module: AnyStr = ''
        self._duration: int = 0

    @property
    def duration(self):
        return self._duration

    @duration.setter
    def duration(self, _):
        self._duration = _

    @property
    def start_time(self):
        return self._start_time

    @start_time.setter
    def start_time(self, _):
        self._start_time = _

    @property
    def end_time(self):
        return self._end_time

    @end_time.setter
    def end_time(self, value):
        self._end_time = value

    @property
    def level(self):
        return self._level

    @level.setter
    def level(self, value):
        self._level = value

    @property
    def case_name(self):
        return self._case_name

    @case_name.setter
    def case_name(self, value):
        self._case_name = value

    @property
    def function_name(self):
        return self._function_name

    @function_name.setter
    def function_name(self, value):
        self._function_name = value

    @property
    def case_module(self):
        return self._case_module

    @case_module.setter
    def case_module(self, value):
        self._case_module = value

    @property
    def result(self, ):
        return self._result

    @result.setter
    def result(self, _):
        self._result = _

    def __str__(self):
        test_result = self._result.__str__()
        result = dict()
        for k, v in self.__dict__.items():
            result[k[1:]] = v
        result['result'] = test_result
        return result


task_detail = TaskDetail()


def pytest_addoption(parser):
    parser.addoption("--env", action="store", default="None", help="指定环境名称")
    parser.addoption("--config", action="append", default=[],
                     help="cmd命令行增加/覆盖本地配置")

def pytest_sessionstart(session):
    task_detail.start_time = int(round(time.time()) * 1000)
    env = session.config.getoption("--env")
    print("运行环境为: %s" % (env if env != 'None' else "测试"))
    configs.reload_config(env=env)
    task_detail.env = env if  env != 'None' else 'test'
    extra_configs: List[AnyStr] = session.config.getoption("--config")
    print("收到外部配置为: %s" % extra_configs)
    for j in extra_configs:
        category, key_value = tuple(j.split("="))
        category = category.split('.')
        configs.add_config(category[0], {category[1]:j})


def pytest_runtest_setup(item):
    case_info = [mark.kwargs for mark in item.iter_markers(name="TestCase")]
    if not case_info:
        case_info = [{}]
    case_info = case_info[0]
    if not task_detail.current_case:
        pass
    else:
        task_detail.case_end()

    task_detail.case_start()
    task_detail.current_case.function_name = item.name
    task_detail.current_case.case_module = case_info.get("case_module", '')
    task_detail.current_case.case_name = case_info.get('case_name', '')
    task_detail.current_case.level = case_info.get("level", '')
    task_detail.current_case.start_time = int(round(time.time()) * 1000)


def pytest_runtest_logreport(report: TestReport):
    setattr(task_detail.current_case.result, report.when, report.passed)
    if report.when == 'teardown':
        task_detail.current_case.end_time = int(round(time.time()) * 1000)
        task_detail.case_end()
    key_words = report.keywords
    if key_words.get("skip") and key_words.get("skip") == 1:
        task_detail.current_case.call = "skipped"
    elif key_words.get("xfail") and key_words.get("xfail") == 1:
        task_detail.current_case.call = "xfail"


def get_result_string(result: List[Dict]):
    return '\n'.join(i.get('function_name') for i in result)


def pytest_sessionfinish(session, exitstatus):
    task_detail.task_end()
    print("测试详情\n")
    success_Case = [i for i in task_detail.cases if i['result'] == 'Success']
    failed_Case = [i for i in task_detail.cases if i['result'] == 'Failed']
    error_Case = [i for i in task_detail.cases if i['result'] == 'Error']
    if success_Case:
        print("\n通过用例:\n" + get_result_string(success_Case))
    if failed_Case:
        print("\n失败用例:\n" + get_result_string(failed_Case))
    if error_Case:
        print("\nError用例:\n" + get_result_string(error_Case))

@pytest.mark.trylast
def pytest_configure(config):
    config.addinivalue_line(
        "markers", "TestCase(**params)")
